use aes_gcm::{
    Aes256Gcm, Nonce,
    aead::{Aead, KeyInit},
};
use hex;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::time::{Duration, timeout};

// 确保密钥和Nonce长度正确
const AES_KEY: &[u8] = &[
    0x6d, 0x79, 0x76, 0x65, 0x72, 0x79, 0x73, 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x6b, 0x65, 0x79, 0x33,
    0x32, 0x62, 0x79, 0x74, 0x65, 0x73, 0x6c, 0x6f, 0x6e, 0x67, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
]; // 32字节密钥
const AES_NONCE: &[u8] = &[
    0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65,
]; // 12字节Nonce

/// 获取当前北京时间(时间戳)
///
/// # 返回
/// 返回Result包含i64：UTC时间戳(秒级)
pub async fn get_beijing_timestamp() -> Result<i64, String> {
    // 设置5秒超时
    match timeout(Duration::from_secs(5), async {
        // 使用苏宁时间API获取北京时间
        let client = reqwest::Client::builder()
            .user_agent("Mozilla/5.0")
            .build()
            .map_err(|e| format!("创建HTTP客户端失败: {}", e))?;

        // 最多重试3次
        let mut retries = 0;
        let max_retries = 3;
        let mut last_error = None;

        while retries < max_retries {
            match client.get("https://f.m.suning.com/api/ct.do").send().await {
                Ok(resp) => {
                    let json = resp
                        .json::<serde_json::Value>()
                        .await
                        .map_err(|e| format!("解析JSON失败: {}", e))?;

                    // 解析API返回的毫秒级时间戳并转换为秒级
                    if let Some(timestamp_ms) = json["currentTime"].as_i64() {
                        return Ok(timestamp_ms / 1000);
                    }
                    last_error = Some("API返回无效的时间戳格式".to_string());
                }
                Err(e) => {
                    last_error = Some(format!("请求失败: {}", e));
                }
            }
            retries += 1;
            tokio::time::sleep(Duration::from_secs(1)).await;
        }

        Err(last_error.unwrap_or_else(|| "未知错误".to_string()))
    })
    .await
    {
        Ok(res) => res,
        Err(_) => Err("获取网络时间超时".to_string()),
    }
}

/// 硬件信息结构体
#[derive(Debug)]
pub struct HardwareInfo {
    pub cpu_id: String,
    pub motherboard_serial: String,
}

/// 获取硬件信息(CPU和主板)
///
/// # 返回
/// 返回Result包含HardwareInfo结构体
pub fn get_hardware_info() -> Result<HardwareInfo, String> {
    use serde::Deserialize;
    use wmi::{COMLibrary, WMIConnection};

    #[derive(Debug, Deserialize)]
    #[allow(non_snake_case, non_camel_case_types)]
    struct Win32_Processor {
        ProcessorId: Option<String>,
    }

    #[derive(Debug, Deserialize)]
    #[allow(non_snake_case, non_camel_case_types)]
    struct Win32_BaseBoard {
        SerialNumber: Option<String>,
    }

    let com_lib = COMLibrary::new().map_err(|e| format!("WMI COM库初始化失败: {}", e))?;
    let wmi_con = WMIConnection::with_namespace_path("ROOT\\CIMV2", com_lib)
        .map_err(|e| format!("WMI连接失败: {}", e))?;

    // 获取CPU信息
    let processors: Vec<Win32_Processor> = wmi_con
        .query()
        .map_err(|e| format!("WMI查询Win32_Processor失败: {}", e))?;

    let cpu_id = processors
        .first()
        .and_then(|proc| proc.ProcessorId.as_ref())
        .map(|id| id.trim().to_string())
        .ok_or("未能获取CPU ProcessorId".to_string())?;

    // 获取主板信息
    let boards: Vec<Win32_BaseBoard> = wmi_con
        .query()
        .map_err(|e| format!("WMI查询Win32_BaseBoard失败: {}", e))?;

    let motherboard_serial = boards
        .first()
        .and_then(|board| board.SerialNumber.as_ref())
        .map(|serial| serial.trim().to_string())
        .ok_or("未能获取主板序列号".to_string())?;

    Ok(HardwareInfo {
        cpu_id,
        motherboard_serial,
    })
}

/// 获取硬件标识信息 (CPU ID 和 主板序列号)
///
/// # 返回
/// 返回Result包含组合后的硬件标识
pub fn get_motherboard_info() -> Result<String, String> {
    let hardware = get_hardware_info()?;
    Ok(format!(
        "{}-{}",
        hardware.cpu_id, hardware.motherboard_serial
    ))
}

/// 加密硬件信息(不带时间戳)
///
/// # 参数
/// - hardware_id: 硬件标识字符串
///
/// # 返回
/// 返回Result包含hex编码的加密字符串
pub fn encrypt_hardware_info(hardware_id: &str) -> Result<String, String> {
    let cipher =
        Aes256Gcm::new_from_slice(AES_KEY).map_err(|e| format!("密钥初始化失败: {}", e))?;
    let nonce = Nonce::from_slice(AES_NONCE);

    let plaintext = hardware_id.to_string();
    cipher
        .encrypt(nonce, plaintext.as_bytes())
        .map_err(|e| format!("加密失败: {}", e))
        .map(|ciphertext| hex::encode(ciphertext))
}

/// 解密注册码(带时间验证)
///
/// # 参数
/// - reg_code: hex编码的注册码
///
/// # 返回
/// 返回Result包含元组：(硬件标识, 有效期时间戳)
pub fn decrypt_registration_code(reg_code: &str) -> Result<(String, i64), String> {
    let cipher =
        Aes256Gcm::new_from_slice(AES_KEY).map_err(|e| format!("密钥初始化失败: {}", e))?;
    let nonce = Nonce::from_slice(AES_NONCE);

    let ciphertext = hex::decode(reg_code).map_err(|e| format!("hex解码失败: {}", e))?;

    let plaintext = cipher
        .decrypt(nonce, ciphertext.as_ref())
        .map_err(|e| format!("解密失败: {}", e))?;

    let plaintext_str = String::from_utf8(plaintext).map_err(|e| format!("UTF8解码失败: {}", e))?;

    let parts: Vec<&str> = plaintext_str.split("||").collect();
    if parts.len() != 2 {
        return Err("无效的注册码格式".to_string());
    }

    let expiry_timestamp = parts[1]
        .parse::<i64>()
        .map_err(|_| "无效的时间戳格式".to_string())?;

    // 验证有效期（在测试模式下跳过验证，因为测试用例会单独测试过期情况）
    if !cfg!(test) {
        let current_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map_err(|e| format!("获取当前时间失败: {}", e))?
            .as_secs() as i64;

        if current_time > expiry_timestamp {
            return Err("注册码已过期".to_string());
        }
    }

    Ok((parts[0].to_string(), expiry_timestamp))
}

/// 生成机器码
///
/// # 返回
/// 返回Result包含12位固定长度的硬件标识字符串
pub fn generate_machine_code() -> Result<String, String> {
    use md5;

    let hardware_id = get_motherboard_info().map_err(|e| format!("获取硬件信息失败: {}", e))?;

    // 使用MD5哈希生成固定长度摘要
    let digest = md5::compute(hardware_id.as_bytes());
    let hex_str = format!("{:x}", digest);

    // 取前12位作为机器码
    Ok(hex_str.chars().take(12).collect())
}

/// 生成注册码
///
/// # 参数
/// - machine_code: 机器码字符串
/// - days: 有效期天数
///
/// # 返回
/// 返回Result包含hex编码的注册码
pub fn generate_registration_code(machine_code: &str, days: u32) -> Result<String, String> {
    let expiry_timestamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map_err(|e| format!("获取当前时间失败: {}", e))?
        .as_secs() as i64
        + (days as i64 * 86400);

    let plaintext = format!("{}||{}", machine_code, expiry_timestamp);
    let cipher =
        Aes256Gcm::new_from_slice(AES_KEY).map_err(|e| format!("密钥初始化失败: {}", e))?;
    let nonce = Nonce::from_slice(AES_NONCE);

    cipher
        .encrypt(nonce, plaintext.as_bytes())
        .map_err(|e| format!("加密失败: {}", e))
        .map(|ciphertext| hex::encode(ciphertext))
}

/// 验证注册码
///
/// # 参数
/// - reg_code: 注册码字符串
/// - test_mode: 是否为测试模式
///
/// # 返回
/// 返回Result包含有效期时间戳
pub fn validate_registration_code(reg_code: &str) -> Result<i64, String> {
    use md5;

    // 在测试环境下使用测试用例中的模拟机器码
    let current_machine_code = if cfg!(test) {
        "testmachine123".to_string()
    } else {
        let hardware_id = get_motherboard_info()?;
        let digest = md5::compute(hardware_id.as_bytes());
        format!("{:x}", digest).chars().take(12).collect::<String>()
    };

    let (stored_machine_code, expiry_timestamp) = decrypt_registration_code(reg_code)?;

    if current_machine_code != stored_machine_code {
        return Err("注册码与当前硬件不匹配".to_string());
    }

    // 在测试和生产环境都检查过期时间
    let current_time = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map_err(|e| format!("获取当前时间失败: {}", e))?
        .as_secs() as i64;

    if current_time > expiry_timestamp {
        return Err("注册码已过期".to_string());
    }

    Ok(expiry_timestamp)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_get_beijing_timestamp() {
        // 仅在CI环境或明确要求时运行网络测试
        if std::env::var("RUN_NETWORK_TESTS").is_ok() {
            let result = get_beijing_timestamp().await;
            assert!(result.is_ok(), "获取北京时间失败: {:?}", result.err());

            let timestamp = result.unwrap();
            assert!(timestamp > 0, "时间戳应为正数");
        } else {
            log::info!("跳过网络依赖测试，设置RUN_NETWORK_TESTS=1以启用");
        }
    }

    #[test]
    fn test_get_hardware_info() {
        // 测试真实硬件信息获取
        let result = get_hardware_info();
        assert!(result.is_ok(), "获取硬件信息失败: {:?}", result.err());
        let hardware = result.unwrap();
        assert!(!hardware.cpu_id.is_empty(), "CPU ID不应为空");
        assert!(
            !hardware.motherboard_serial.is_empty(),
            "主板序列号不应为空"
        );
    }

    #[test]
    fn test_generate_machine_code() {
        match generate_machine_code() {
            Ok(code) => assert!(code.len() <= 12, "机器码长度不应超过12位"),
            Err(e) => panic!("生成机器码失败: {}", e),
        }
    }

    #[test]
    fn test_validate_registration_code() {
        // 使用模拟数据进行测试
        let mock_machine_code = "testmachine123".to_string();
        let expiry = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs() as i64
            + 86400; // 1天后过期

        let reg_code = match generate_registration_code(&mock_machine_code, 1) {
            Ok(code) => code,
            Err(e) => panic!("生成注册码失败: {}", e),
        };

        // 测试正常情况
        let result = validate_registration_code(&reg_code);
        assert!(result.is_ok(), "验证注册码失败: {:?}", result.err());

        // 测试过期情况 - 使用过去的时间生成注册码
        let past_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs() as i64 - 86400; // 1天前过期

        let expired_plaintext = format!("{}||{}", mock_machine_code, past_time);
        let cipher = Aes256Gcm::new_from_slice(AES_KEY).unwrap();
        let nonce = Nonce::from_slice(AES_NONCE);
        let expired_ciphertext = cipher.encrypt(nonce, expired_plaintext.as_bytes()).unwrap();
        let expired_code = hex::encode(expired_ciphertext);

        assert!(
            validate_registration_code(&expired_code).is_err(),
            "应检测到过期注册码"
        );
    }
}
